iT邦幫忙

2023 iThome 鐵人賽

DAY 19
0
Vue.js

業主說給你30天學會Vue系列 第 19

V19_從Vue的範例做中學(3)_CRUD

  • 分享至 

  • xImage
  •  

V19_從Vue的範例做中學(3)_CRUD

今天來練習一下,在網頁資料中常見的 CRUD
CRUD 是 Create(新增), Read(讀取), Update(更新), Delete(刪除 四個字的縮寫,
也是在資料庫操作中,最常見的4種操作,若以SQL的語法來看,有時會稱為
新增(INSERT), 查詢(UPDATE), 刪除(DELETE), 修改(SELECT)

先來練習網頁前端的操作
參考在Vue官網上的範例
https://vuejs.org/examples/#crud

仍然是以 Composition+SFC 的模式為主
App.vue


<script setup>
import { ref, reactive, computed, watch } from 'vue'

const names = reactive(['Emil, Hans', 'Mustermann, Max', 'Tisch, Roman'])
const selected = ref('')
const prefix = ref('')
const first = ref('')
const last = ref('')

const filteredNames = computed(() =>
  names.filter((n) =>
    n.toLowerCase().startsWith(prefix.value.toLowerCase())
  )
)

watch(selected, (name) => {
  ;[last.value, first.value] = name.split(', ')
})

function create() {
  if (hasValidInput()) {
    const fullName = `${last.value}, ${first.value}`
    if (!names.includes(fullName)) {
      names.push(fullName)
      first.value = last.value = ''
    }
  }
}

function update() {
  if (hasValidInput() && selected.value) {
    const i = names.indexOf(selected.value)
    names[i] = selected.value = `${last.value}, ${first.value}`
  }
}

function del() {
  if (selected.value) {
    const i = names.indexOf(selected.value)
    names.splice(i, 1)
    selected.value = first.value = last.value = ''
  }
}

function hasValidInput() {
  return first.value.trim() && last.value.trim()
}
</script>

<template>
  <div><input v-model="prefix" placeholder="Filter prefix"></div>

  <select size="5" v-model="selected">
    <option v-for="name in filteredNames" :key="name">{{ name }}</option>
  </select>

  <label>Name: <input v-model="first"></label>
  <label>Surname: <input v-model="last"></label>

  <div class="buttons">
    <button @click="create">Create</button>
    <button @click="update">Update</button>
    <button @click="del">Delete</button>
  </div>
</template>

<style>
* {
  font-size: inherit;
}

input {
  display: block;
  margin-bottom: 10px;
}

select {
  float: left;
  margin: 0 1em 1em 0;
  width: 14em;
}

.buttons {
  clear: both;
}

button + button {
  margin-left: 5px;
}
</style>

執行的結果如圖
https://ithelp.ithome.com.tw/upload/images/20231003/20152098iAtXpUCuWD.png

//----------------------------------------
我們先從<template>的內容看起,
可以看到這次要綁定的項目比較多

<input v-model="prefix" placeholder="Filter prefix"> 

<select size="5" v-model="selected">
  <option v-for="name in filteredNames" :key="name">{{ name }}</option>
</select>

<label>Name: <input v-model="first"></label>
<label>Surname: <input v-model="last"></label>

<div class="buttons">
  <button @click="create">Create</button>
  <button @click="update">Update</button>
  <button @click="del">Delete</button>
</div>

v-model類的有 prefix,selected,first,last

相對的宣告

const selected = ref('')
const prefix = ref('')
const first = ref('')
const last = ref('')

v-for類的有 "name in filteredNames"

const names = reactive(['Emil, Hans', 'Mustermann, Max', 'Tisch, Roman'])
names 是先宣告有3個元素 的 reactive()

const filteredNames = computed(() =>
  names.filter((n) =>
    n.toLowerCase().startsWith(prefix.value.toLowerCase())
  )
)

這裡使用了 computed()
是指過濾的條件為names元素的文字先轉小寫文字後的字首與輸入的文字轉小寫文字相同,
回傳給filteredNames

這裡也可以變成 n.toLowerCase().includes(prefix.value.toLowerCase())
使用 includes 來過濾names元素的文字有包含prefix的元素

接著 <option v-for="name in filteredNames" :key="name">{{ name }}</option>
透過 v-for 取出 filteredNames 的每一個元素 name
同時 呈現在 {{ name }},並綁定到 :key 屬性

v-bind類的有 :key="name"

@click類的有 create,update,del

@click="create"

function create() {
  if (hasValidInput()) {
    const fullName = `${last.value}, ${first.value}`
    if (!names.includes(fullName)) {
      names.push(fullName)
      first.value = last.value = ''
    }
  }
}

先檢查輪入資料是否正確 hasValidInput(),如果 names 名單中 沒有fullName的話
就將fullName加入到names, names.push(fullName)

最後將輸入欄位 first.value,last.value 清空

再來是
@click="update"

function update() {
  if (hasValidInput() && selected.value) {
    const i = names.indexOf(selected.value)
    names[i] = selected.value = `${last.value}, ${first.value}`
  }
}

hasValidInput() 先檢查輸入資料,同時 selected.value 有資料
讀取 selected.value在 names 名單中的索引值 i
然後將 selected.value = ${last.value}, ${first.value}傳給names[i]`

最後是 @click="del"

function del() {
  if (selected.value) {
    const i = names.indexOf(selected.value)
    names.splice(i, 1)
    selected.value = first.value = last.value = ''
  }
}

如果 selected.value 有資料的話
取得 selected.value 在 names 陣列大得索引值 i
接著從names的索引值i 的位置,刪除一個元素 names.splice(i, 1)

最後將輸入欄位 first.value,last.valueselect選單中的選擇值 清空

最後是 hasValidInput()

function hasValidInput() {
  return first.value.trim() && last.value.trim()
}

就是在檢查 first 及 last輸入欄位 去除空白字元後是否仍有輸入值

再來有看到 一個 watch()

watch(selected, (name) => {
  ;[last.value, first.value] = name.split(', ')
})

watch的對象是 selected,當有變動時會執行將該變動的name當作參數輸入,
執行 ;[last.value, first.value] = name.split(', ')

name.split(', ') 是將name 以 , 為分隔字元 分隔成陣列
其傳到 [last.value, first.value]

//------------------------
這裡發現陣列開始前出現;
這個分號是為了讓有出現直接用陣列方括號來操作時,會與前一個程式碼結合造成錯誤,
JS規定可以在陣列方括號前加上;以作為程式碼之間隔開之用

看到這邊,想到JS也有一些建議的程式編寫格式的準則
JavaScript Standard Style
https://standardjs.com/readme-zhtw
https://standardjs.com/rules-zhtw#semicolons

雖然不是官方的,但是也提供了很完整的編寫建議,
也可以加裝在常用的編輯器上

其中有提到因為在程式碼格式中,最大習慣的改變是 程式碼句尾可以不用加分號
這個不加分號的方式,在遇到以( 、 [ 或 `當作程式的開頭時,容易誤認為是與前一程式串接在一起
所以有了在 陣列方括號前加上;的建議

像是以下這種狀況

watch(selected, (name) => {
  console.log(selected.value)
  [last.value, first.value] = name.split(', ')
})

會被編輯成

watch(selected, (name) => {
  console.log(selected.value)[last.value, first.value] = name.split(', ')
})

而產生錯誤
所以要改寫成

watch(selected, (name) => {
  console.log(selected.value);
  [last.value, first.value] = name.split(', ')
})

這樣雖然可以但是不建議
要寫成這樣才是建議的

watch(selected, (name) => {
  console.log(selected.value)
  ;[last.value, first.value] = name.split(', ')
})

第一種會理解成是程式碼句尾加上;
第二種則是理解成做為一種提示作用,代表這裡有以方括號為程式開始的程式碼

//------------------
由以上的解析後,發現CRUD都是陣列元素的操作

同時 互相綁定,監看及 更新的關係很密切,在維護上要對彼此的連動關係很清楚,
才應確保資料可以正確操作


上一篇
V18_Vue的Composition_API的功能清單
下一篇
V20_Vue in W3School
系列文
業主說給你30天學會Vue31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言